﻿#include "QCefContext.h"
#include "QCefProtocol.h"
#include "QCefWidget.h"

//////////////////////////////////////////////////////////////////////////
//
QCefContext * QCefContext::ms_instance = nullptr;


//////////////////////////////////////////////////////////////////////////
//
QCefContext::QCefContext()
    : QObject(nullptr)
    , m_timer(nullptr)
    , m_msgLoop(nullptr)
{
    QCEF_DEBUG();

    //注册自定义类型
    qRegisterMetaType<QStrMultiMap>("QStrMultiMap");
    qRegisterMetaType<QStrMultiMap>("QStrMultiMap&");
    qRegisterMetaType<QStrMultiMap>("QStrMap");
    qRegisterMetaType<QStrMultiMap>("QStrMap&");

    //
    connect(QCoreApplication::instance(), &QCoreApplication::aboutToQuit, this, &QCefContext::onAboutToQuit);

    //保存第一个实例
    if (ms_instance == nullptr)
    {
        ms_instance = this;
    }
}

QCefContext::~QCefContext()
{
    QCEF_DEBUG();

    //
    if (ms_instance == this)
    {
        ms_instance = nullptr;
    }

    //
    m_cefBrowserApp = nullptr;
    m_cefRenderApp = nullptr;
    m_cefOtherApp = nullptr;    

    // shutdown the cef
    CefShutdown();

    if (m_timer != nullptr)
    {
        delete m_timer;
        m_timer = nullptr;
    }

    if (m_msgLoop != nullptr)
    {
        delete m_msgLoop;
        m_msgLoop = nullptr;
    }
}


int QCefContext::init()
{
    QCEF_DEBUG();

    //
    CefEnableHighDPISupport();

    //CEF 命令行参数
    CefMainArgs args(::GetModuleHandle(nullptr));

    CefRefPtr<CefCommandLine> cmd = CefCommandLine::CreateCommandLine();
    cmd->InitFromString(::GetCommandLineW());

    //
    CefRefPtr<QCefApp> cefApp;

    //
    if (cmd->HasSwitch(kProcessType))
    {
        //render进程与browser进程的exe文件相同，进程不同。
        //render.exe ...

        const std::string & strProcessType = cmd->GetSwitchValue(kProcessType);
        if (strProcessType == kRendererProcess)
        {
            m_cefRenderApp = new QCefRenderApp();
            cefApp = m_cefRenderApp;
        }
        else
        {
            m_cefOtherApp = new QCefOtherApp();
            cefApp = m_cefOtherApp;
        }

        int exit_code = CefExecuteProcess(args, cefApp, nullptr);
        if (exit_code >= 0)
        {
            // The sub-process has completed so return here.
            //return exit_code;
            return 1;
        }

        return 1;
    }
    else
    {
        //browser.exe ...

         //msg loop
        if (m_cefSetting.getMultiThreadMsgLoop() == false)
        {
            //timer_msg_loop, win_msg_loop, multi_thread_msg_loop 三选一
#ifdef QT_CEF_TIMER_MSG_LOOP
            //定时器消息循环
            m_timer = new QTimer();
            m_timer->setInterval(30);
            connect(m_timer, &QTimer::timeout, this, &QCefContext::onMessageLoop);
#endif  //QT_CEF_TIMER_MSG_LOOP

#ifdef QT_CEF_WIN_MSG_LOOP
            //窗口消息循环
            m_msgLoop = new QCefWinMsgLoop();
#endif  //QT_CEF_WIN_MSG_LOOP
        }

        m_cefBrowserApp = new QCefBrowserApp();
        cefApp = m_cefBrowserApp;

#ifdef QT_CEF_SINGLE_PROCESS
        //单一进程模式，render进程与browser进程的为同一个进程。
        int exit_code = CefExecuteProcess(args, cefApp, nullptr);
        if (exit_code >= 0)
        {
            return 1;
        }
#endif // QT_CEF_SINGLE_PROCESS
        
        //初始化
        if (!CefInitialize(args, m_cefSetting.getCefSettings(), cefApp, nullptr))
        {
            return -1;
        }

        //启动消息循环
        if (m_timer != nullptr)
        {
            m_timer->start();
        }
        else if (m_msgLoop != nullptr)
        {
            m_msgLoop->Run();
        }
    }

    return 0;
}


int QCefContext::fini()
{
    QCEF_DEBUG();

    if (m_timer != nullptr)
    {
        m_timer->stop();
    }

    if (m_msgLoop != nullptr)
    {
        m_msgLoop->Quit();
    }

    return 0;
}

void QCefContext::onAboutToQuit()
{
    QCEF_DEBUG("begin");

    // check whether can exit now
    if (!m_cefBrowserApp->isSafeToExit())
    {
        // launch an event loop to wait for the clean process
        // of CEF browsers resource
        QEventLoop loop;

        // create an idle timer to check whether the CEF resource has been
        // cleaned up. If yes then exit the event loop to continue the
        // application exit process
        QTimer timer;
        this->connect(&timer, &QTimer::timeout, [&]() {
            QCEF_DEBUG("timeout");

            // if all browser were closed and there is only one reference to the
            // CefBrowserClient object (only referred by QCefContextPrivate instance),
            // we can quit safely
            if (m_cefBrowserApp->isSafeToExit())
            {
                loop.quit();
            }
        });

        // start the timer
        timer.start(1000);

        // enter the event loop
        loop.exec();
    }

    QCEF_DEBUG("end");
}

void QCefContext::onMessageLoop()
{
    QCEF_DEBUG();

    CefDoMessageLoopWork();
}

